package com.xnyzc.lhy.order.entity.common.bill;

import com.alibaba.fastjson.JSON;
import com.xnyzc.lhy.common.component.redis.RedisCommon;
import com.xnyzc.lhy.common.constant.RedisPrefixConstant;
import com.xnyzc.lhy.common.entity.Qw;
import com.xnyzc.lhy.common.exception.PangException;
import com.xnyzc.lhy.common.penum.EErrorCode;
import com.xnyzc.lhy.common.util.BigDecimalUtils;
import com.xnyzc.lhy.common.util.CheckUtil;
import com.xnyzc.lhy.common.util.DateUtil;
import com.xnyzc.lhy.order.entity.common.gps.GpsCommon;
import com.xnyzc.lhy.order.entity.common.gps.GpsHistoryInfo;
import com.xnyzc.lhy.order.entity.common.gps.HistoryEntity;
import com.xnyzc.lhy.order.entity.common.order.OrderCommon;
import com.xnyzc.lhy.order.entity.order.OaCOrder;
import com.xnyzc.lhy.order.entity.system.OaSysBillRules;
import com.xnyzc.lhy.order.entity.system.OaSysBillSpecialRules;
import com.xnyzc.lhy.order.feign.amap.IAmapService;
import com.xnyzc.lhy.order.mapper.sidebar.OaSysOfficeMapper;
import com.xnyzc.lhy.order.mapper.system.OaSysBillRulesMapper;
import com.xnyzc.lhy.order.mapper.system.OaSysBillSpecialRulesMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author: huxuekuo
 * @Date: 2019-08-13 12:43
 * @Description: 计费规则私有方法类
 */
@Component
@Slf4j
public class BillRulesCommon {
    @Autowired
    OaSysBillRulesMapper billRulesMapper;

    @Autowired
    OaSysBillSpecialRulesMapper billSpecialRulesMapper;

    @Autowired
    RedisCommon redisCommon;

    @Autowired
    OaSysOfficeMapper sysOfficeMapper;

    @Autowired
    IAmapService amapService;

    @Autowired
    GpsCommon gpsCommon;

    @Autowired
    IAmapService iAmapService;

    @Autowired
    OrderCommon orderCommon;

    /**
     * 公里差值,用来判断是否需要启动兜底方案
     */
    private static final Double MILEAGEDIFF = 3.0d;


    /**
     * 里程计费
     *
     * @param id             计费规则
     * @param currentTime    当前时间
     * @param currentMileage 当前里程
     * @param totailMileage  总里程
     * @param tag            多租户标志符
     * @return
     */
    public BigDecimal mileageFee(String id, Date currentTime, BigDecimal currentMileage, BigDecimal totailMileage, String tag) {
        BigDecimal currentMileageFee = new BigDecimal(0);
        //当前里程小于基础里程不算取里程费
        OaSysBillRules oaSysBillRules = getRulesById(id, tag);
        Integer startMileage = oaSysBillRules.getStartMileage();
        if (totailMileage.intValue() <= startMileage) {
            return currentMileageFee;
        }
        //判断是否为特殊时段
        List<OaSysBillSpecialRules> oaSysBillSpecialRules = selectBillSpecialRules(oaSysBillRules.getRulesId());
        for (OaSysBillSpecialRules oaSysBillSpecialRule : oaSysBillSpecialRules) {
            if (DateUtil.isRange(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), currentTime)) {
                // 不是远途,是特殊时段
                return currentMileage.multiply(oaSysBillSpecialRule.getSpecialMileageFee()).setScale(2, BigDecimal.ROUND_HALF_UP);
            }
        }
        //普通时段,非远途费
        return currentMileage.multiply(oaSysBillRules.getMileageFee()).setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 获取远途费用
     *
     * @param totalMileage   总里程
     * @param currentMileage 当前历程
     * @param currentTime    当前时间
     * @param id             计费规则ID
     * @param tag            标签
     * @return
     */
    public BigDecimal getDistanceFee(BigDecimal totalMileage, BigDecimal currentMileage, Date currentTime, String id, String tag) {
        BigDecimal currentMileageFee = new BigDecimal(0);
        //当前里程小于基础里程不算取里程费
        OaSysBillRules oaSysBillRules = getRulesById(id, tag);
        //判断是否为远途
        if (oaSysBillRules.getDistanceKm().doubleValue() < totalMileage.doubleValue()) {
            //判断是否是特殊时间段
            List<OaSysBillSpecialRules> oaSysBillSpecialRules = selectBillSpecialRules(oaSysBillRules.getRulesId());
            for (OaSysBillSpecialRules oaSysBillSpecialRule : oaSysBillSpecialRules) {
                if (DateUtil.isRange(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), currentTime)) {
                    return currentMileage.multiply(oaSysBillSpecialRule.getSpecialDistanceFee()).setScale(2, BigDecimal.ROUND_HALF_UP);
                }
            }
            //不在特殊时间段
            return currentMileage.multiply(oaSysBillRules.getDistanceFee()).setScale(2, BigDecimal.ROUND_HALF_UP);
        }
        return currentMileageFee;
    }


    /**
     * 获取计费规则
     *
     * @param id
     * @param tag
     * @return
     */
    public OaSysBillRules startFee(String id, String tag) {
        OaSysBillRules oaSysBillRules = getRulesById(id, tag);
        return oaSysBillRules;
    }

    /**
     * 获取时长费
     *
     * @return
     */
    public BigDecimal timeFee(String id, BigDecimal currentTime, BigDecimal totalTime, String tag, Date startDate) {
        BigDecimal currentTimeFee = new BigDecimal(0);
        //当前里程小于基础里程不算取时长费
        OaSysBillRules oaSysBillRules = getRulesById(id, tag);
        //如果总时长小于起步时长不算取时长费
        BigDecimal startTime = BigDecimalUtils.value(oaSysBillRules.getStartTime());
        if (startTime.max(totalTime).equals(startTime)) {
            return currentTimeFee;
        }
        //判断是否在特殊时间段内
        List<OaSysBillSpecialRules> oaSysBillSpecialRules = selectBillSpecialRules(oaSysBillRules.getRulesId());
        if (oaSysBillSpecialRules.size() > 0) {
            for (OaSysBillSpecialRules oaSysBillSpecialRule : oaSysBillSpecialRules) {
                if (DateUtil.isRange(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), startDate)) {
                    return currentTime.multiply(oaSysBillSpecialRule.getSpecialTimeFee()).setScale(2, BigDecimal.ROUND_UP);
                }
            }
        }
        //如果不在特殊时间内获取
        return currentTime.multiply(oaSysBillRules.getTimeFee()).setScale(2, BigDecimal.ROUND_UP);
    }

    /**
     * 获取时长费
     *
     * @return
     */
    public OaCOrder timeFee(OaCOrder oaCOrder) {
        OaSysBillRules oaSysBillRules = getRulesById(String.valueOf(oaCOrder.getRulesId()), oaCOrder.getTag());
        //如果总时长小于起步时长不算取时长费
        BigDecimal startTime = BigDecimalUtils.value(oaSysBillRules.getStartTime());

        Date lastTime = oaCOrder.getLastTime();
        Date nowDate = new Date();

        String orderNo = oaCOrder.getOrderNo();
        //如果上一次上传时间为空,设置定点时间
        if (CheckUtil.objIsEmpty(lastTime)) {
            lastTime = new Date();
            oaCOrder.setLastTime(lastTime);
        }
        //获取分钟
        BigDecimal minute = BigDecimal.valueOf(((nowDate.getTime() - lastTime.getTime()) / 1000d) / 60d);
        //整数部分(需要就算钱的部分)
        BigDecimal Integer = minute.setScale(0, BigDecimal.ROUND_DOWN);
        log.info("timeFee**{} 需要计算的时长----->{} ", orderNo, Integer);
        //获取小数部分
        BigDecimal fractionalPart = minute.remainder(BigDecimal.ONE);
        log.info("timeFee**{} 小数部分----->{} ", orderNo, fractionalPart);

        BigDecimal orderStartTime = oaCOrder.getStartTime();
        if (CheckUtil.objIsEmpty(orderStartTime)) {
            orderStartTime = new BigDecimal(0);
            oaCOrder.setStartTime(orderStartTime);
        }
        //大于1进行计算,小于1不进行计算(必须 是整数类型)
        if (Integer.doubleValue() >= 1) {
            orderStartTime = orderStartTime.add(Integer);
            oaCOrder.setStartTime(orderStartTime);
            //加一分钟
            Calendar rightNow = Calendar.getInstance();
            rightNow.setTime(lastTime);
            rightNow.add(Calendar.MINUTE, Integer.intValue());
            oaCOrder.setLastTime(rightNow.getTime());
        } else {
            //如果等于1,代表最后一次结算,不能直接返回
            if (oaCOrder.getIsFinalSett().equals(0)) {
                log.info("timeFee**{} 需要计算的时长小于一分钟无需计算 ", orderNo);
                return oaCOrder;
            }
        }

        if (startTime.doubleValue() >= orderStartTime.doubleValue()) {
            log.info("timeFee**{} 当前总时间小于起步时间,起步时间:{},总时间:{} ", orderNo, startTime, oaCOrder.getDurationTime());
            return oaCOrder;
        }
        //设置默认值
        BigDecimal totalTime = oaCOrder.getDurationTime();
        //最后一次设置金额去掉起步价
        if (CheckUtil.objIsEmpty(totalTime)) {
            totalTime = new BigDecimal(0);
        }
        totalTime = totalTime.add(Integer);
        oaCOrder.setDurationTime(totalTime);
        //此次时长费
        BigDecimal timeFee = new BigDecimal(0);
        BigDecimal durationFee = oaCOrder.getDurationFee();
        if (CheckUtil.objIsEmpty(durationFee)) {
            durationFee = new BigDecimal(0);
        }
        //判断是否在特殊时段内
        List<OaSysBillSpecialRules> oaSysBillSpecialRules = selectBillSpecialRules(oaSysBillRules.getRulesId());
        if (oaSysBillSpecialRules.size() > 0) {
            for (OaSysBillSpecialRules oaSysBillSpecialRule : oaSysBillSpecialRules) {
                if (DateUtil.isRange(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), nowDate)) {
                    timeFee = Integer.multiply(oaSysBillSpecialRule.getSpecialTimeFee()).setScale(2, BigDecimal.ROUND_HALF_UP);
                    durationFee = durationFee.add(timeFee);
                    //判断是否是最后一次进行时长计算
                    if (oaCOrder.getIsFinalSett().equals(1)) {
                        if (fractionalPart.doubleValue() > 0.000) {
                            durationFee = durationFee.add(oaSysBillSpecialRule.getSpecialTimeFee()).setScale(2, BigDecimal.ROUND_HALF_DOWN);
                            BigDecimal add1 = oaCOrder.getDurationTime().add(new BigDecimal(1));
                            oaCOrder.setDurationTime(add1);
                        }
                    }
                    oaCOrder.setDurationFee(durationFee);
                    log.info("timeFee**{} 设置后的费用: {}", orderNo, durationFee);
                    return oaCOrder;
                }
            }
        }
        timeFee = Integer.multiply(oaSysBillRules.getTimeFee());
        //设置此次时长费
        durationFee = durationFee.add(timeFee);
        //判断是否是最后一次进行时长计算
        if (oaCOrder.getIsFinalSett().equals(1)) {
            if (fractionalPart.doubleValue() > 0.000) {
                durationFee = durationFee.add(oaSysBillRules.getTimeFee()).setScale(2, BigDecimal.ROUND_HALF_DOWN);
                BigDecimal add1 = oaCOrder.getDurationTime().add(new BigDecimal(1));
                oaCOrder.setDurationTime(add1);
            }
        }
        oaCOrder.setDurationFee(durationFee);
        log.info("timeFee**{} 设置后的费用: {}", orderNo, durationFee);
        return oaCOrder;
    }

    public OaSysBillRules getRulesById(String id, String tag) {
        String key = RedisPrefixConstant.BILL_RULES + id;
        //判断Redis中是否存在,如果存在获取
        if (redisCommon.hashKey(key)) {
            String billRulesJson = redisCommon.getStr(key);
            return JSON.parseObject(billRulesJson, OaSysBillRules.class);
        }
        OaSysBillRules oaSysBillRules = this.selectBillRules(Long.valueOf(id));
        if (CheckUtil.objIsEmpty(oaSysBillRules)) {
            throw PangException.create(EErrorCode.noData);
        }
        //保存到Redis
        redisCommon.set(key, JSON.toJSONString(oaSysBillRules));
        return oaSysBillRules;
    }

    /**
     * 根据ID获取特殊计费规则
     *
     * @param id 特殊计费规则Id
     * @return
     */
    public List<OaSysBillSpecialRules> selectBillSpecialRules(Long id) {
        String key = RedisPrefixConstant.BILL_SPECIAL_RULES + id;
        //如果redis中存在从redis中获取
        if (redisCommon.hashKey(key)) {
            String billSpecialRulesJson = redisCommon.getStr(key);
            List<OaSysBillSpecialRules> list = JSON.parseArray(billSpecialRulesJson, OaSysBillSpecialRules.class);
            return list;
        }
        //redis中不存在从数据库中获取
        Qw add = Qw.create().eq(OaSysBillSpecialRules.RULES_ID, id);
        List<OaSysBillSpecialRules> list = billSpecialRulesMapper.selectList(add);
        redisCommon.set(key, JSON.toJSONString(list));
        return list;
    }

    public OaSysBillSpecialRules getBillSpecialRulesById(Long id) {
        return billSpecialRulesMapper.selectById(id);
    }


    /**
     * 获取抽佣后的费用
     *
     * @param totalBillFee
     * @param rulesId
     * @return
     */
    public BigDecimal getDrawFee(BigDecimal totalBillFee, String rulesId) {
        BigDecimal drawFee = new BigDecimal(0);
        //当前里程小于基础里程不算取时长费
        OaSysBillRules oaSysBillRules = getRulesById(rulesId, "lhy");
        if (CheckUtil.objIsEmpty(oaSysBillRules)) {
            log.info("未获取到计费规则!");
            return drawFee;
        }
        BigDecimal drawRatio = oaSysBillRules.getDrawRatio();
        if (CheckUtil.objIsEmpty(drawRatio)) {
            log.info("未获取到抽佣比例!");
            return drawFee;
        }
        return totalBillFee.subtract(totalBillFee.multiply(drawRatio));
    }

    /**
     * 计算里程费
     *
     * @param oaCOrder
     * @return
     */
    public OaCOrder getMileageFee(OaCOrder oaCOrder) {
        OaSysBillRules oaSysBillRules = getRulesById(String.valueOf(oaCOrder.getRulesId()), oaCOrder.getTag());
        if (CheckUtil.objIsEmpty(oaSysBillRules)) {
            log.info("未获取到计费规则,无法继续!");
            return oaCOrder;
        }
        Integer startMileage = oaSysBillRules.getStartMileage();
        Date currentDate = new Date();
        String orderNo = oaCOrder.getOrderNo();
        //上一次计费米数
        BigDecimal lastMileageFee = oaCOrder.getLastMileage();
        if (CheckUtil.objIsEmpty(lastMileageFee)) {
            lastMileageFee = new BigDecimal(0);
            oaCOrder.setLastMileage(lastMileageFee);
        }
        //当前行驶总米数据
        BigDecimal currentToatlMileage = oaCOrder.getCurrentToatlMileage();
        if (CheckUtil.objIsEmpty(currentToatlMileage)) {
            currentToatlMileage = new BigDecimal(0);
            oaCOrder.setCurrentToatlMileage(currentToatlMileage);
        }
        //此次区间里程
        BigDecimal mileage = BigDecimal.valueOf(currentToatlMileage.subtract(lastMileageFee).doubleValue() / 1000d);
        //整数部分(需要就算钱的部分)
        BigDecimal Integer = mileage.setScale(0, BigDecimal.ROUND_DOWN);
        log.info("mileage**{} 需要计算的里程----->{} ", orderNo, Integer);
        //获取小数部分
        BigDecimal fractionalPart = mileage.remainder(BigDecimal.ONE);
        log.info("mileage**{} 小数部分----->{} ", orderNo, fractionalPart);
        BigDecimal startOrderMileage = oaCOrder.getStartMileage();
        if (CheckUtil.objIsEmpty(startOrderMileage)) {
            startOrderMileage = new BigDecimal(0);
            oaCOrder.setStartMileage(startOrderMileage);
        }
        //大于1进行计算,小于1不进行计算(必须 是整数类型)
        if (Integer.doubleValue() >= 1) {
            startOrderMileage = startOrderMileage.add(Integer);
            oaCOrder.setStartMileage(startOrderMileage);
            //设置上一次计算里程
            oaCOrder.setLastMileage(oaCOrder.getLastMileage().add(Integer.multiply(new BigDecimal(1000))));
        } else {
            //如果等于1,代表最后一次结算,不能直接返回
            if (oaCOrder.getIsFinalSett().equals(0)) {
                log.info("currentMileageFee**{} 需要计算的公里小于一公里无需计算 ", orderNo);
                return oaCOrder;
            }
        }
        if (startMileage >= startOrderMileage.intValue()) {
            log.info("currentMileageFee**{} 当前总时间小于起步起步公里,起步里程:{},总里程:{} ", orderNo, startMileage, oaCOrder.getMilageDistance());
            return oaCOrder;
        }
        //当前总里程
        BigDecimal totalMileage = oaCOrder.getMilageDistance();
        //设置默认值
        if (CheckUtil.objIsEmpty(totalMileage)) {
            totalMileage = new BigDecimal(0);
        }
        oaCOrder.setMilageDistance(totalMileage.add(Integer));
        //此次时长费
        BigDecimal currentMileageFee = new BigDecimal(0);
        BigDecimal mileageFee = oaCOrder.getMileageFee();
        if (CheckUtil.objIsEmpty(mileageFee)) {
            mileageFee = new BigDecimal(0);
        }
        //判断是否在特殊时段内
        List<OaSysBillSpecialRules> oaSysBillSpecialRules = selectBillSpecialRules(oaSysBillRules.getRulesId());
        if (oaSysBillSpecialRules.size() > 0) {
            for (OaSysBillSpecialRules oaSysBillSpecialRule : oaSysBillSpecialRules) {
                if (DateUtil.isRange(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), currentDate)) {
                    currentMileageFee = Integer.multiply(oaSysBillSpecialRule.getSpecialMileageFee()).setScale(2, BigDecimal.ROUND_HALF_UP);
                    if (currentMileageFee.doubleValue() >= 0) {
                        mileageFee = mileageFee.add(currentMileageFee);
                    }
                    //判断是否是最后一次进行时长计算
                    if (oaCOrder.getIsFinalSett().equals(1)) {
                        //获取小数点后的里程数
                        fractionalPart = fractionalPart.setScale(2, BigDecimal.ROUND_HALF_DOWN);
                        BigDecimal decimal = fractionalPart.multiply(oaSysBillRules.getMileageFee()).setScale(2, BigDecimal.ROUND_HALF_DOWN);
                        mileageFee = mileageFee.add(decimal).setScale(2, BigDecimal.ROUND_HALF_DOWN);
                        oaCOrder.setMilageDistance(oaCOrder.getMilageDistance().add(fractionalPart).setScale(2, BigDecimal.ROUND_HALF_DOWN));
                    }
                    oaCOrder.setMileageFee(mileageFee);
                    log.info("currentMileageFee**{} 设置后的费用: {}", orderNo, mileageFee);
                    return oaCOrder;
                }
            }
        }
        currentMileageFee = Integer.multiply(oaSysBillRules.getMileageFee());
        //设置此次时长费
        if (currentMileageFee.doubleValue() >= 0) {
            mileageFee = mileageFee.add(currentMileageFee);
        }
        //判断是否是最后一次进行时长计算
        if (oaCOrder.getIsFinalSett().equals(1)) {
            //获取小数点后的里程数
            fractionalPart = fractionalPart.setScale(2, BigDecimal.ROUND_HALF_DOWN);
            BigDecimal decimal = fractionalPart.multiply(oaSysBillRules.getMileageFee()).setScale(2, BigDecimal.ROUND_HALF_DOWN);
            mileageFee = mileageFee.add(decimal).setScale(2, BigDecimal.ROUND_HALF_DOWN);
            oaCOrder.setMilageDistance(oaCOrder.getMilageDistance().add(fractionalPart).setScale(2, BigDecimal.ROUND_HALF_DOWN));
        }
        oaCOrder.setMileageFee(mileageFee);
        log.info("currentMileageFee**{} 设置后的费用: {}", orderNo, mileageFee);
        return oaCOrder;
    }

    /**
     * 获取远途费用
     *
     * @param oaCOrder
     * @return
     */
    public OaCOrder getDistanceFee(OaCOrder oaCOrder) {
        //获取计价规则
        OaSysBillRules oaSysBillRules = getRulesById(String.valueOf(oaCOrder.getRulesId()), oaCOrder.getTag());
        //当前总里程
        BigDecimal milageDistance = oaCOrder.getMilageDistance();
        //大于多少公里收取远途费
        Integer distanceKm = oaSysBillRules.getDistanceKm();
        if (CheckUtil.objIsEmpty(distanceKm)) {
            distanceKm = 0;
        }

        //远途费 =  (起步里程 + 行驶里程) - 大于多少公里收取远途费 里程
        Integer startMileage = oaSysBillRules.getStartMileage();
        if (CheckUtil.objIsEmpty(startMileage)) {
            startMileage = 0;
        }
        milageDistance = milageDistance.add(new BigDecimal(startMileage));

        BigDecimal subtract = milageDistance.subtract(new BigDecimal(distanceKm)).setScale(2, BigDecimal.ROUND_HALF_DOWN);
        if (subtract.doubleValue() > 0) {
            //获取远途费
            BigDecimal multiply = oaSysBillRules.getDistanceFee().multiply(subtract).setScale(2, BigDecimal.ROUND_HALF_DOWN);
            oaCOrder.setDistanceFee(multiply);
            oaCOrder.setEmptyDistance(subtract);
        }
        return oaCOrder;
    }


    /**
     * 到达目的地清点费用
     *
     * @param rulesId 计费规则Id
     * @return
     */
    public HashMap<String, BigDecimal> arriveEndCountFee(Long rulesId, Date endTime, OaCOrder oaCOrder, BigDecimal totalMileage) {
        HashMap<String, BigDecimal> initFeeMap = initFeeVar();
        Long gpsId = oaCOrder.getGpsId();
        Long tid = oaCOrder.getTid();
        Long serviceId = oaCOrder.getServiceId();
        String orderNo = oaCOrder.getOrderNo();
        // 获取计价规则,如果为空退出吧
        OaSysBillRules oaSysBillRules = selectBillRules(rulesId);
        if (CheckUtil.objIsEmpty(oaSysBillRules)) {
            log.info("ORDER_HISTORY 订单号:[{}],未获取计价规则,rulesId:[{}],无法开始计费", orderNo, rulesId);
            return initFeeMap;
        }
        // 查询出纠偏绑路后的全程点位和里程
        HistoryEntity initTotalHistory = this.getHistoryEntity(oaCOrder, endTime, 0);
        if (CheckUtil.objIsEmpty(initTotalHistory)) {
            log.info("ORDER_HISTORY 订单号:[{}],未获取到精细的轨迹信息,gpsId:[{}],tid:[{}],serviceId:[{}]", orderNo, gpsId, tid, serviceId);
            return arriveEndCountFee(oaCOrder, initTotalHistory);
        }
        // 判断是否上传了总里程
        if (CheckUtil.objIsEmpty(totalMileage)) {
            log.info("ORDER_HISTORY 订单号:[{}],当前上传的里程是null值,totalMileage:[{}]", orderNo, totalMileage);
            return arriveEndCountFee(oaCOrder, initTotalHistory);
        }
        // 进行米转换为公里
        BigDecimal hitsoryMileage = BigDecimalUtils.divideRoundHalfUp(2, initTotalHistory.getDistance(), BigDecimal.valueOf(1000));
        totalMileage = BigDecimalUtils.divideRoundHalfUp(2, totalMileage, BigDecimal.valueOf(1000));
        // 获取里程差距绝对值
        double mileageAbs = Math.abs(hitsoryMileage.subtract(totalMileage).doubleValue());
        // 小于固定差值不需要进行兜底方案
        if (mileageAbs < MILEAGEDIFF) {
            log.info("ORDER_HISTORY 订单号:[{}],当前里程差距在:[{}]范围以内,不需要兜底,,当前里程差距:[{}]", orderNo, MILEAGEDIFF, mileageAbs);
            return arriveEndCountFee(oaCOrder, initTotalHistory);
        }
        // 进行兜底方案
        List<Object> filterTotalHitsorys = new ArrayList<>();
        // 查询出无过滤数据
        HistoryEntity noFilterTotalHistory = this.getHistoryEntity(oaCOrder, endTime, 1);
        // 如果为空不需要过滤
        if (CheckUtil.objIsEmpty(noFilterTotalHistory)) {
            log.info("ORDER_HISTORY 订单号:[{}],无过滤获取到轨迹信息为空:[{}]", orderNo, noFilterTotalHistory);
            return arriveEndCountFee(oaCOrder, initTotalHistory);
        }
        // 更改速度equals0.0的订单
        boolean uploadSuc = true;
        List<GpsHistoryInfo> gpsHistoryInfos = noFilterTotalHistory.getGpsHistoryInfos();
        for (int i = 0; i < gpsHistoryInfos.size(); i++) {
            GpsHistoryInfo gpsHistoryInfo = gpsHistoryInfos.get(i);
            Double speed = gpsHistoryInfo.getSpeed();
            if (CheckUtil.objIsEmpty(speed) || speed < (1.0)) {
                gpsHistoryInfo.setSpeed(255d);
                filterTotalHitsorys.add(gpsHistoryInfo);
            }
            // 每满100点,上传一次
            if (filterTotalHitsorys.size() >= 100) {
                if (!gpsCommon.uploadTrack(gpsId, tid, serviceId, filterTotalHitsorys)) {
                    uploadSuc = false;
                }
                filterTotalHitsorys.clear();
            }
            // 最后一次上传
            if (gpsHistoryInfos.size() - 1 == i) {
                if (filterTotalHitsorys.size() > 0) {
                    if (!gpsCommon.uploadTrack(gpsId, tid, serviceId, filterTotalHitsorys)) {
                        uploadSuc = false;
                    }
                    filterTotalHitsorys.clear();
                }
            }
        }
        if (uploadSuc) {
            // 重新拉去计算费用
            HistoryEntity historyDistance = this.getHistoryEntity(oaCOrder, endTime, 0);
            log.info("ORDER_HISTORY 订单号:[{}],过滤后重新拉去过滤数据:[{}]", orderNo, historyDistance);
            // 计算费用
            return arriveEndCountFee(oaCOrder, historyDistance);
        }
        log.info("ORDER_HISTORY 订单号:[{}],因为上传中有失败的节点,使用原始过滤数据进行计算金额 原始数据JSON:[{}]", orderNo, initTotalHistory);
        return arriveEndCountFee(oaCOrder, initTotalHistory);
    }

    /**
     * 初始化费用变量
     *
     * @return
     */
    public HashMap<String, BigDecimal> initFeeVar() {
        HashMap<String, BigDecimal> countFee = new HashMap<>();
        countFee.put("mileageFee", BigDecimal.valueOf(BigDecimal.ZERO.intValue()));
        countFee.put("specialDistance", BigDecimal.valueOf(BigDecimal.ZERO.intValue()));
        countFee.put("timeFee", BigDecimal.valueOf(BigDecimal.ZERO.intValue()));
        countFee.put("specialTime", BigDecimal.valueOf(BigDecimal.ZERO.intValue()));
        countFee.put("distanceFee", BigDecimal.valueOf(BigDecimal.ZERO.intValue()));
        countFee.put("distance", BigDecimal.valueOf(BigDecimal.ZERO.intValue()));
        return countFee;
    }

    public HistoryEntity getHistoryEntity(OaCOrder oaCOrder, Date endTime, Integer type) {
        // 查询出纠偏绑路后的全程点位和里程
        HistoryEntity totalHistory = gpsCommon.getHistoryEntity(oaCOrder, oaCOrder.getOrderTime(), endTime, type);
        List<Map<String, HistoryEntity>> specialHistoryList = new ArrayList<>();
        List<OaSysBillSpecialRules> oaSysBillSpecialRules = selectBillSpecialRules(oaCOrder.getRulesId());
        //获取全部特殊时间段
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");
        String timeBegin = dateFormat.format(oaCOrder.getOrderTime());
        String timeEnd = dateFormat.format(endTime);
        if (oaSysBillSpecialRules.size() > 0) {
            for (OaSysBillSpecialRules oaSysBillSpecialRule : oaSysBillSpecialRules) {
                //当前当前时间是否在此时间段内
                Map<String, Date> dateMap = DateUtil.inDateGetStartAndEnd(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), timeBegin, timeEnd);
                if (CheckUtil.mapIsEmpty(dateMap)) {
                    continue;
                }
                // 调用 查询轨迹接口判断是否有里程
                HistoryEntity historyDistance = gpsCommon.getHistoryDistance(oaCOrder.getGpsId(), oaCOrder.getTid(), oaCOrder.getServiceId(), dateMap.get("start"), dateMap.get("end"), type);
                if (CheckUtil.objIsNotEmpty(historyDistance)) {
                    Map<String, HistoryEntity> historyEntityMap = new HashMap<>();
                    historyEntityMap.put(String.valueOf(oaSysBillSpecialRule.getSpecialRulesId()), historyDistance);
                    specialHistoryList.add(historyEntityMap);
                }
            }
        }
        totalHistory.setSpecialHistory(specialHistoryList);
        return totalHistory;
    }

    public HashMap<String, BigDecimal> arriveEndCountFee(OaCOrder oaCOrder, HistoryEntity historyEntity) {
        // 特殊时段 里程和时长
        HashMap<String, BigDecimal> initFeeVar = initFeeVar();
        BigDecimal totalMil = BigDecimalUtils.divideRoundHalfUp(2, historyEntity.getDistance(), BigDecimal.valueOf(1000));
        BigDecimal totalTime = BigDecimalUtils.divideRoundHalfUp(2, historyEntity.getTime(), BigDecimal.valueOf(1000), BigDecimal.valueOf(60));
        BigDecimal distanceMil = new BigDecimal(totalMil.doubleValue());
        // 判断是否为节假日，获取计价规则
        OaSysBillRules oaSysBillRules = selectBillRules(oaCOrder.getRulesId());
        if (CheckUtil.objIsEmpty(oaSysBillRules)) {
            return initFeeVar;
        }
        // 设置总里程
        BigDecimal subtractMil = totalMil.subtract(BigDecimal.valueOf(oaSysBillRules.getStartMileage()));
        totalMil = subtractMil.doubleValue() > 0 ? subtractMil : BigDecimal.valueOf(0);
        // 设置总时长
        BigDecimal subtractTime = totalTime.subtract(BigDecimal.valueOf(oaSysBillRules.getStartTime()));
        totalTime = subtractTime.doubleValue() > 0 ? subtractTime : BigDecimal.valueOf(0);
        // 设置特殊时段费用
        List<Map<String, HistoryEntity>> specialHistory = historyEntity.getSpecialHistory();
        if (CheckUtil.collectionIsNotEmpty(specialHistory)) {
            for (Map<String, HistoryEntity> historyEntityMap : specialHistory) {
                // 传统的Map迭代方式
                for (Map.Entry<String, HistoryEntity> entry : historyEntityMap.entrySet()) {
                    OaSysBillSpecialRules billSpecialRulesById = getBillSpecialRulesById(Long.valueOf(entry.getKey()));
                    if (CheckUtil.objIsEmpty(billSpecialRulesById)) {
                        continue;
                    }
                    HistoryEntity value = entry.getValue();
                    if (CheckUtil.objIsEmpty(value)) {
                        continue;
                    }
                    BigDecimal specialMil = BigDecimalUtils.divideRoundHalfUp(2, value.getDistance(), BigDecimal.valueOf(1000));
                    BigDecimal specialTime = BigDecimalUtils.divideRoundHalfUp(2, value.getTime(), BigDecimal.valueOf(1000), BigDecimal.valueOf(60));
                    // 设置特殊时段里程费
                    if (specialMil.doubleValue() > totalMil.doubleValue()) {
                        specialMil = totalMil;
                    }
                    BigDecimal multiply = specialMil.multiply(billSpecialRulesById.getSpecialMileageFee());
                    totalMil = totalMil.subtract(specialMil);
                    if (totalMil.doubleValue() < 0) {
                        totalMil = new BigDecimal(0);
                    }
                    initFeeVar.put("mileageFee", initFeeVar.get("mileageFee").add(multiply).setScale(2, BigDecimal.ROUND_HALF_UP));
                    initFeeVar.put("specialDistance", initFeeVar.get("specialDistance").add(specialMil).setScale(2, BigDecimal.ROUND_HALF_UP));
                    // 设置特殊时段是时长费
                    if (specialTime.doubleValue() > totalTime.doubleValue()) {
                        specialTime = totalTime;
                    }
                    multiply = specialTime.multiply(billSpecialRulesById.getSpecialTimeFee());
                    initFeeVar.put("timeFee", initFeeVar.get("timeFee").add(multiply).setScale(2, BigDecimal.ROUND_HALF_UP));
                    initFeeVar.put("specialTime", initFeeVar.get("specialTime").add(specialTime).setScale(2, BigDecimal.ROUND_HALF_UP));
                    totalTime = totalTime.subtract(specialTime);
                    if (totalTime.doubleValue() < 0) {
                        totalTime = new BigDecimal(0);
                    }
                }
            }
        }
        BigDecimal multiply = totalMil.multiply(oaSysBillRules.getMileageFee());
        initFeeVar.put("mileageFee", initFeeVar.get("mileageFee").add(multiply).setScale(2, BigDecimal.ROUND_HALF_UP));
        initFeeVar.put("specialDistance", initFeeVar.get("specialDistance").add(totalMil).setScale(2, BigDecimal.ROUND_HALF_UP));
        multiply = totalTime.multiply(oaSysBillRules.getTimeFee().setScale(2, BigDecimal.ROUND_HALF_UP));
        initFeeVar.put("timeFee", initFeeVar.get("timeFee").add(multiply).setScale(2, BigDecimal.ROUND_HALF_UP));
        initFeeVar.put("specialTime", initFeeVar.get("specialTime").add(totalTime).setScale(2, BigDecimal.ROUND_HALF_UP));
        // 计算远途费
        BigDecimal distanceKm = new BigDecimal(oaSysBillRules.getDistanceKm());
        if (distanceMil.doubleValue() > distanceKm.doubleValue()) {
            BigDecimal subtract = distanceMil.subtract(distanceKm).setScale(2, BigDecimal.ROUND_HALF_UP);
            if (subtract.doubleValue() > 0.0) {
                initFeeVar.put("distanceFee", subtract.multiply(oaSysBillRules.getDistanceFee()).setScale(2, BigDecimal.ROUND_HALF_UP));
                initFeeVar.put("distance", subtract);
            }
        }
        return initFeeVar;
    }

    /**
     * 普通时间段计费
     *
     * @param billRules
     * @return
     */
    public HashMap<String, BigDecimal> ordinaryTime(OaSysBillRules billRules, BigDecimal timeBig, BigDecimal mileageBig, HashMap<String, BigDecimal> result, BigDecimal totalMileag) {
        BigDecimal startFee = billRules.getStartFee();
        if (!result.containsKey("startFee")) {
            result.put("startFee", startFee);
        }
        //获取多余里程费用
        BigDecimal computingMileageFee = new BigDecimal(0);
        if (mileageBig.doubleValue() > 0.0) {
            computingMileageFee = mileageBig.multiply(billRules.getMileageFee());
            BigDecimal specialDistanceFee = result.get("specialDistanceFee");
            result.put("specialDistanceFee", specialDistanceFee.add(computingMileageFee));
        } else {
            //防止出现负数
            mileageBig = new BigDecimal(0);
        }
        //获取多余时长费
        BigDecimal specialTimeFee = new BigDecimal(0);
        if (timeBig.doubleValue() > 0.0) {
            specialTimeFee = timeBig.multiply(billRules.getTimeFee());
            BigDecimal specialTimeFees = result.get("specialTimeFee");
            result.put("specialTimeFee", specialTimeFees.add(specialTimeFee));
        } else {
            //防止出现负数
            timeBig = new BigDecimal(0);
        }
        //获取远途里程计算费用
        BigDecimal distanceKm = new BigDecimal(billRules.getDistanceKm());
        BigDecimal distanceKmFee = new BigDecimal(0);
        if (totalMileag.doubleValue() > distanceKm.doubleValue()) {
            //获取远途里程
            BigDecimal distanceMileage = totalMileag.subtract(distanceKm);
            distanceKmFee = distanceMileage.multiply(billRules.getDistanceFee());
            result.put("emptyFee", distanceKmFee);
            result.put("emptyDistance", distanceMileage.multiply(new BigDecimal(1000)));
        }
        //计价里程转换为米
        BigDecimal specialDistance = result.get("specialDistance");
        result.put("specialDistance", specialDistance.add(mileageBig));
        //计价时间转换为秒
        BigDecimal specialTime1 = result.get("specialTime");
        result.put("specialTime", specialTime1.add(timeBig));
        return result;
    }

    /**
     * 根据计费规则Id,查询计费规则
     *
     * @param rulesId
     * @return
     */
    private OaSysBillRules selectBillRules(Long rulesId) {
        OaSysBillRules oaSysBillRules = billRulesMapper.selectByRulesId(rulesId);
        if (CheckUtil.objIsNotEmpty(oaSysBillRules)) {
            return oaSysBillRules;
        }
        return null;
    }


}
